#include <rclcpp/rclcpp.hpp>
#include <opentelemetry/trace/provider.h>
#include <opentelemetry/exporters/ostream/span_exporter.h>
#include <opentelemetry/sdk/trace/simple_processor.h>
#include <opentelemetry/sdk/trace/tracer_provider.h>
#include <opentelemetry/sdk/trace/samplers/always_on.h>
#include <opentelemetry/sdk/resource/resource.h>
#include <opentelemetry/nostd/shared_ptr.h>
#include <opentelemetry/nostd/unique_ptr.h>

#include <chrono>
#include <thread>
#include <string>
#include <vector>
#include <memory>
#include <sys/resource.h>
#include <unistd.h>
#include <iomanip>
#include <sstream>
#include <random>

namespace trace_api = opentelemetry::trace;
namespace trace_sdk = opentelemetry::sdk::trace;
namespace resource_sdk = opentelemetry::sdk::resource;
namespace nostd = opentelemetry::nostd;

// Performance monitoring utilities
struct PerformanceStats {
    // Timing statistics
    std::chrono::nanoseconds total_time;
    std::chrono::nanoseconds min_time;
    std::chrono::nanoseconds max_time;
    std::chrono::nanoseconds avg_time;
    size_t operation_count;
    
    // Memory statistics
    long memory_before_kb;
    long memory_after_kb;
    long memory_peak_kb;
    long memory_delta_kb;
    
    // CPU statistics
    long cpu_time_user_before;
    long cpu_time_system_before;
    long cpu_time_user_after;
    long cpu_time_system_after;
    long cpu_time_user_delta;
    long cpu_time_system_delta;
    
    void reset() {
        total_time = std::chrono::nanoseconds(0);
        min_time = std::chrono::nanoseconds::max();
        max_time = std::chrono::nanoseconds(0);
        avg_time = std::chrono::nanoseconds(0);
        operation_count = 0;
        
        memory_before_kb = 0;
        memory_after_kb = 0;
        memory_peak_kb = 0;
        memory_delta_kb = 0;
        
        cpu_time_user_before = 0;
        cpu_time_system_before = 0;
        cpu_time_user_after = 0;
        cpu_time_system_after = 0;
        cpu_time_user_delta = 0;
        cpu_time_system_delta = 0;
    }
    
    void calculateAverages() {
        if (operation_count > 0) {
            avg_time = std::chrono::nanoseconds(total_time.count() / operation_count);
        }
        memory_delta_kb = memory_after_kb - memory_before_kb;
        cpu_time_user_delta = cpu_time_user_after - cpu_time_user_before;
        cpu_time_system_delta = cpu_time_system_after - cpu_time_system_before;
    }
};

class OpenTelemetryPerformanceMonitor {
public:
    static long getCurrentMemoryKB() {
        struct rusage usage;
        getrusage(RUSAGE_SELF, &usage);
        return usage.ru_maxrss;
    }
    
    static std::pair<long, long> getCurrentCPUTime() {
        struct rusage usage;
        getrusage(RUSAGE_SELF, &usage);
        return {usage.ru_utime.tv_sec * 1000000 + usage.ru_utime.tv_usec,
                usage.ru_stime.tv_sec * 1000000 + usage.ru_stime.tv_usec};
    }
    
    static std::string formatDuration(std::chrono::nanoseconds duration) {
        auto ns = duration.count();
        if (ns < 1000) {
            return std::to_string(ns) + " ns";
        } else if (ns < 1000000) {
            return std::to_string(ns / 1000) + " μs";
        } else if (ns < 1000000000) {
            return std::to_string(ns / 1000000) + " ms";
        } else {
            return std::to_string(ns / 1000000000) + " s";
        }
    }
    
    static std::string formatMemory(long memory_kb) {
        if (memory_kb < 1024) {
            return std::to_string(memory_kb) + " KB";
        } else if (memory_kb < 1024 * 1024) {
            return std::to_string(memory_kb / 1024) + " MB";
        } else {
            return std::to_string(memory_kb / (1024 * 1024)) + " GB";
        }
    }
    
    static void printStats(const std::string& test_name, const PerformanceStats& stats) {
        std::cout << "\n=== " << test_name << " Performance Results ===" << std::endl;
        std::cout << "Operations: " << stats.operation_count << std::endl;
        std::cout << "Total Time: " << formatDuration(stats.total_time) << std::endl;
        std::cout << "Min Time: " << formatDuration(stats.min_time) << std::endl;
        std::cout << "Max Time: " << formatDuration(stats.max_time) << std::endl;
        std::cout << "Avg Time: " << formatDuration(stats.avg_time) << std::endl;
        std::cout << "Memory Before: " << formatMemory(stats.memory_before_kb) << std::endl;
        std::cout << "Memory After: " << formatMemory(stats.memory_after_kb) << std::endl;
        std::cout << "Memory Delta: " << formatMemory(stats.memory_delta_kb) << std::endl;
        std::cout << "Memory Peak: " << formatMemory(stats.memory_peak_kb) << std::endl;
        std::cout << "CPU User Time: " << stats.cpu_time_user_delta << " μs" << std::endl;
        std::cout << "CPU System Time: " << stats.cpu_time_system_delta << " μs" << std::endl;
        std::cout << "Throughput: " << std::fixed << std::setprecision(2) 
                  << (stats.operation_count * 1000000000.0 / stats.total_time.count()) << " ops/sec" << std::endl;
    }
};

// Initialize OpenTelemetry tracing
void InitTracer() {
    auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(
        new opentelemetry::exporter::trace::OStreamSpanExporter);
    
    auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(
        new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
    
    auto resource = resource_sdk::Resource::Create({
        {"service.name", "opentelemetry-performance-monitor"},
        {"service.version", "1.0.0"},
        {"service.instance.id", "performance-monitor-001"}
    });
    
    auto sampler = std::unique_ptr<trace_sdk::Sampler>(
        new trace_sdk::AlwaysOnSampler);
    
    auto provider = nostd::shared_ptr<trace_api::TracerProvider>(
        new trace_sdk::TracerProvider(std::move(processor), resource, std::move(sampler)));
    
    trace_api::Provider::SetTracerProvider(provider);
}

class PerformanceTestNode : public rclcpp::Node {
public:
    PerformanceTestNode() : Node("performance_test_node") {
        // Initialize OpenTelemetry
        InitTracer();
        tracer_ = trace_api::Provider::GetTracerProvider()->GetTracer("performance-test");
        
        RCLCPP_INFO(this->get_logger(), "OpenTelemetry Performance Monitor started");
        RCLCPP_INFO(this->get_logger(), "Running comprehensive performance tests...");
        
        // Run all performance tests
        runBasicSpanCreationTest();
        runHighFrequencySpanTest();
        runLargeAttributeTest();
        runConcurrentSpanTest();
        runDeepNestingTest();
        runMemoryStressTest();
        
        RCLCPP_INFO(this->get_logger(), "All performance tests completed!");
    }

private:
    void runBasicSpanCreationTest() {
        RCLCPP_INFO(this->get_logger(), "Running basic span creation test...");
        
        PerformanceStats stats;
        stats.reset();
        
        // Record initial state
        stats.memory_before_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_before = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_before = cpu_before.first;
        stats.cpu_time_system_before = cpu_before.second;
        
        const size_t iterations = 10000;
        
        auto start_time = std::chrono::high_resolution_clock::now();
        
        for (size_t i = 0; i < iterations; ++i) {
            auto span_start = std::chrono::high_resolution_clock::now();
            
            auto span = tracer_->StartSpan("basic_span_test", {
                {"test.iteration", static_cast<int64_t>(i)},
                {"test.type", "basic_span_creation"}
            });
            
            auto scope = tracer_->WithActiveSpan(span);
            span->SetAttribute("test.data", "sample_data_" + std::to_string(i));
            span->AddEvent("test_event");
            span->SetStatus(trace_api::StatusCode::kOk, "Test completed");
            span->End();
            
            auto span_end = std::chrono::high_resolution_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(span_end - span_start);
            
            stats.total_time += duration;
            stats.min_time = std::min(stats.min_time, duration);
            stats.max_time = std::max(stats.max_time, duration);
            stats.operation_count++;
        }
        
        auto end_time = std::chrono::high_resolution_clock::now();
        
        // Record final state
        stats.memory_after_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_after = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_after = cpu_after.first;
        stats.cpu_time_system_after = cpu_after.second;
        
        stats.calculateAverages();
        OpenTelemetryPerformanceMonitor::printStats("Basic Span Creation", stats);
    }
    
    void runHighFrequencySpanTest() {
        RCLCPP_INFO(this->get_logger(), "Running high frequency span test...");
        
        PerformanceStats stats;
        stats.reset();
        
        stats.memory_before_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_before = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_before = cpu_before.first;
        stats.cpu_time_system_before = cpu_before.second;
        
        const size_t iterations = 50000;
        
        for (size_t i = 0; i < iterations; ++i) {
            auto span_start = std::chrono::high_resolution_clock::now();
            
            auto span = tracer_->StartSpan("high_freq_span", {
                {"iteration", static_cast<int64_t>(i)},
                {"timestamp", std::chrono::duration_cast<std::chrono::milliseconds>(
                    std::chrono::system_clock::now().time_since_epoch()).count()}
            });
            
            span->End();
            
            auto span_end = std::chrono::high_resolution_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(span_end - span_start);
            
            stats.total_time += duration;
            stats.min_time = std::min(stats.min_time, duration);
            stats.max_time = std::max(stats.max_time, duration);
            stats.operation_count++;
        }
        
        stats.memory_after_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_after = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_after = cpu_after.first;
        stats.cpu_time_system_after = cpu_after.second;
        
        stats.calculateAverages();
        OpenTelemetryPerformanceMonitor::printStats("High Frequency Span Creation", stats);
    }
    
    void runLargeAttributeTest() {
        RCLCPP_INFO(this->get_logger(), "Running large attribute test...");
        
        PerformanceStats stats;
        stats.reset();
        
        stats.memory_before_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_before = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_before = cpu_before.first;
        stats.cpu_time_system_before = cpu_before.second;
        
        const size_t iterations = 1000;
        const size_t attributes_per_span = 100;
        
        for (size_t i = 0; i < iterations; ++i) {
            auto span_start = std::chrono::high_resolution_clock::now();
            
            auto span = tracer_->StartSpan("large_attribute_span", {
                {"iteration", static_cast<int64_t>(i)}
            });
            
            // Add many attributes
            for (size_t j = 0; j < attributes_per_span; ++j) {
                span->SetAttribute("attr_" + std::to_string(j), 
                    "value_" + std::to_string(i) + "_" + std::to_string(j));
            }
            
            span->End();
            
            auto span_end = std::chrono::high_resolution_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(span_end - span_start);
            
            stats.total_time += duration;
            stats.min_time = std::min(stats.min_time, duration);
            stats.max_time = std::max(stats.max_time, duration);
            stats.operation_count++;
        }
        
        stats.memory_after_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_after = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_after = cpu_after.first;
        stats.cpu_time_system_after = cpu_after.second;
        
        stats.calculateAverages();
        OpenTelemetryPerformanceMonitor::printStats("Large Attribute Span", stats);
    }
    
    void runConcurrentSpanTest() {
        RCLCPP_INFO(this->get_logger(), "Running concurrent span test...");
        
        PerformanceStats stats;
        stats.reset();
        
        stats.memory_before_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_before = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_before = cpu_before.first;
        stats.cpu_time_system_before = cpu_before.second;
        
        const size_t num_threads = 4;
        const size_t spans_per_thread = 2500;
        std::vector<std::thread> threads;
        
        auto start_time = std::chrono::high_resolution_clock::now();
        
        for (size_t t = 0; t < num_threads; ++t) {
            threads.emplace_back([this, spans_per_thread, t, &stats]() {
                for (size_t i = 0; i < spans_per_thread; ++i) {
                    auto span_start = std::chrono::high_resolution_clock::now();
                    
                    auto span = tracer_->StartSpan("concurrent_span", {
                        {"thread_id", static_cast<int64_t>(t)},
                        {"span_id", static_cast<int64_t>(i)}
                    });
                    
                    span->SetAttribute("thread.data", "thread_" + std::to_string(t) + "_span_" + std::to_string(i));
                    span->End();
                    
                    auto span_end = std::chrono::high_resolution_clock::now();
                    auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(span_end - span_start);
                    
                    // Thread-safe stats update (simplified)
                    stats.operation_count++;
                }
            });
        }
        
        for (auto& thread : threads) {
            thread.join();
        }
        
        auto end_time = std::chrono::high_resolution_clock::now();
        stats.total_time = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time);
        
        stats.memory_after_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_after = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_after = cpu_after.first;
        stats.cpu_time_system_after = cpu_after.second;
        
        stats.calculateAverages();
        OpenTelemetryPerformanceMonitor::printStats("Concurrent Span Creation", stats);
    }
    
    void runDeepNestingTest() {
        RCLCPP_INFO(this->get_logger(), "Running deep nesting test...");
        
        PerformanceStats stats;
        stats.reset();
        
        stats.memory_before_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_before = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_before = cpu_before.first;
        stats.cpu_time_system_before = cpu_before.second;
        
        const size_t iterations = 1000;
        const size_t nesting_depth = 10;
        
        for (size_t i = 0; i < iterations; ++i) {
            auto span_start = std::chrono::high_resolution_clock::now();
            
            std::vector<nostd::shared_ptr<trace_api::Span>> spans;
            
            // Create nested spans
            for (size_t depth = 0; depth < nesting_depth; ++depth) {
                auto span = tracer_->StartSpan("nested_span_level_" + std::to_string(depth), {
                    {"iteration", static_cast<int64_t>(i)},
                    {"depth", static_cast<int64_t>(depth)}
                });
                spans.push_back(span);
            }
            
            // End spans in reverse order
            for (auto it = spans.rbegin(); it != spans.rend(); ++it) {
                (*it)->End();
            }
            
            auto span_end = std::chrono::high_resolution_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(span_end - span_start);
            
            stats.total_time += duration;
            stats.min_time = std::min(stats.min_time, duration);
            stats.max_time = std::max(stats.max_time, duration);
            stats.operation_count++;
        }
        
        stats.memory_after_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_after = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_after = cpu_after.first;
        stats.cpu_time_system_after = cpu_after.second;
        
        stats.calculateAverages();
        OpenTelemetryPerformanceMonitor::printStats("Deep Nesting", stats);
    }
    
    void runMemoryStressTest() {
        RCLCPP_INFO(this->get_logger(), "Running memory stress test...");
        
        PerformanceStats stats;
        stats.reset();
        
        stats.memory_before_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_before = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_before = cpu_before.first;
        stats.cpu_time_system_before = cpu_before.second;
        
        const size_t iterations = 5000;
        std::vector<nostd::shared_ptr<trace_api::Span>> span_pool;
        
        auto start_time = std::chrono::high_resolution_clock::now();
        
        // Create many spans and keep them alive
        for (size_t i = 0; i < iterations; ++i) {
            auto span = tracer_->StartSpan("memory_stress_span", {
                {"iteration", static_cast<int64_t>(i)},
                {"memory_test", true}
            });
            
            span->SetAttribute("large_data", std::string(1000, 'A' + (i % 26)));
            span_pool.push_back(span);
            
            if (i % 1000 == 0) {
                // End some spans to simulate real usage
                for (size_t j = 0; j < 100 && !span_pool.empty(); ++j) {
                    span_pool.back()->End();
                    span_pool.pop_back();
                }
            }
        }
        
        // End remaining spans
        for (auto& span : span_pool) {
            span->End();
        }
        
        auto end_time = std::chrono::high_resolution_clock::now();
        stats.total_time = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time);
        stats.operation_count = iterations;
        
        stats.memory_after_kb = OpenTelemetryPerformanceMonitor::getCurrentMemoryKB();
        auto cpu_after = OpenTelemetryPerformanceMonitor::getCurrentCPUTime();
        stats.cpu_time_user_after = cpu_after.first;
        stats.cpu_time_system_after = cpu_after.second;
        
        stats.calculateAverages();
        OpenTelemetryPerformanceMonitor::printStats("Memory Stress Test", stats);
    }

    nostd::shared_ptr<trace_api::Tracer> tracer_;
};

int main(int argc, char* argv[]) {
    rclcpp::init(argc, argv);
    
    auto node = std::make_shared<PerformanceTestNode>();
    
    RCLCPP_INFO(node->get_logger(), "Starting OpenTelemetry Performance Monitor...");
    RCLCPP_INFO(node->get_logger(), "This will test OpenTelemetry's resource usage and performance impact");
    
    rclcpp::spin(node);
    rclcpp::shutdown();
    
    return 0;
}
